package com.aude.sharding.jdbc;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import com.aude.sharding.exception.shardingException;
import com.aude.sharding.jdbc.adapter.UnsupportedStatementAdapter;
import com.aude.sharding.jdbc.model.ExecuteCommand;
import com.aude.sharding.jdbc.model.ExecuteMethod;
import com.aude.sharding.jdbc.model.ResultSetHandler;
import com.aude.sharding.jdbc.model.StatementCreateCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TStatement extends UnsupportedStatementAdapter {
	
	private Logger logger = LoggerFactory.getLogger(TStatement.class);

	protected TConnection connection;
	
	protected StatementCreateCommand statementCreateCommand;

	/**
	 * 真正执行的Statement是不能被重用的，应为在一个statement执行2次查询返回两个ResultSet,如果重用了,
	 * 第一个ResultSet就会被Close 所以每次执行SQL,都需要把前一次的Statement全关闭掉,防止OOM。
	 */
	protected Set<Statement> openedStatement = new HashSet<Statement>();

	/**
	 * 上一次执行SQL的结果
	 */
	protected TResultSet currentResultSet;
	
	protected int currentUpdateCount;
	
	protected ResultSet generatedKeysResultSet;

	/**
	 * 是否为关闭的Statement
	 */
	protected boolean closed;

	/*----------------暂时都采用默认值0表示采用默认值----------------*/
	private int maxFieldSize; // 字符和二进制列的字节限制
	private int maxRows;// 最大行数
	private int queryTimeOut;
	private int direction = ResultSet.FETCH_FORWARD;// 默认,不支持修改
	private int fetchSize;
	/*-----------------------------------------------*/

	public TStatement(TConnection connection, StatementCreateCommand statementCreateCommand) {
		this.connection = connection;
		this.statementCreateCommand = statementCreateCommand;
	}

	public void closeOpenedStatement() throws SQLException {
		for (Statement stmt : openedStatement) {
			stmt.close();
			openedStatement.remove(stmt);
		}
	}

	public void addOpenedStatement(Statement stmt) {
		openedStatement.add(stmt);
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> T unwrap(Class<T> iface) throws SQLException {
		try {
			return (T) this;
		} catch (Exception e) {
			throw new SQLException(e);
		}
	}

	@Override
	public boolean isWrapperFor(Class<?> iface) throws SQLException {
		return this.getClass().isAssignableFrom(iface);
	}
	
	@Override
	public boolean execute(String sql) throws SQLException {
		checkClosed();
		clearLastResultSet();
		ExecuteCommand command = new ExecuteCommand(ExecuteMethod.execute_sql, new Object[]{sql}, false);
		ResultSetHandler handler = connection.executeSql(statementCreateCommand, command, null, this);
		return handler.isResultType();
	}

	@Override
	public ResultSet executeQuery(String sql) throws SQLException {
		checkClosed();
		clearLastResultSet();
		ExecuteCommand command = new ExecuteCommand(ExecuteMethod.executeQuery_sql, new Object[]{sql}, false);
		connection.executeSql(statementCreateCommand, command, null, this);
		return this.currentResultSet;
	}

	@Override
	public int executeUpdate(String sql) throws SQLException {
		checkClosed();
		clearLastResultSet();
		ExecuteCommand command = new ExecuteCommand(ExecuteMethod.executeUpdate_sql, new Object[]{sql}, false);
		connection.executeSql(statementCreateCommand, command, null, this);
		return this.currentUpdateCount;
	}

	/**
	 * 获取由于执行此 Statement 对象而创建的所有自动生成的键。如果此 Statement 对象没有生成任何键，则返回空的 ResultSet 对象。
	 * 注：如果未指定表示自动生成键的列，则 JDBC 驱动程序实现将确定最能表示自动生成键的列。
	 */
	@Override
	public ResultSet getGeneratedKeys() throws SQLException {
		// 如果是使用了execute(String sql, int autoGeneratedKeys)等方法,则抓取
		// TODO 如果多个insert则抛异常
		return this.generatedKeysResultSet;
	}

	@Override
	public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
		checkClosed();
		clearLastResultSet();
		ExecuteCommand command = new ExecuteCommand(ExecuteMethod.executeUpdate_sql_autoGeneratedKeys, new Object[]{sql, autoGeneratedKeys}, true);
		connection.executeSql(statementCreateCommand, command, null, this);
		return this.currentUpdateCount;
	}

	/**
	 * 执行给定的 SQL 语句，并通知驱动程序在给定数组中指示的自动生成的键应该可用于获取。
	 * 此数组包含目标表中的列的索引，该目标表包含应该可获取的自动生成的键。
	 * 如果该 SQL 语句不是 INSERT 语句，或者不是可以返回自动生成键的 SQL 语句（这些语句的列表是特定于供应商的）
	 * 则驱动程序将忽略该数组。
	 */
	@Override
	public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
		checkClosed();
		clearLastResultSet();
		ExecuteCommand command = new ExecuteCommand(ExecuteMethod.executeUpdate_sql_columnIndexes, new Object[]{sql, columnIndexes}, true);
		connection.executeSql(statementCreateCommand, command, null, this);
		return this.currentUpdateCount;
	}

	@Override
	public int executeUpdate(String sql, String[] columnNames) throws SQLException {
		checkClosed();
		clearLastResultSet();
		ExecuteCommand command = new ExecuteCommand(ExecuteMethod.executeUpdate_sql_columnNames, new Object[]{sql, columnNames}, true);
		connection.executeSql(statementCreateCommand, command, null, this);
		return this.currentUpdateCount;
	}

	@Override
	public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
		checkClosed();
		clearLastResultSet();
		ExecuteCommand command = new ExecuteCommand(ExecuteMethod.execute_sql_autoGeneratedKeys, new Object[]{sql, autoGeneratedKeys}, true);
		ResultSetHandler handler = connection.executeSql(statementCreateCommand, command, null, this);
		return handler.isResultType();
	}

	@Override
	public boolean execute(String sql, int[] columnIndexes) throws SQLException {
		checkClosed();
		clearLastResultSet();
		ExecuteCommand command = new ExecuteCommand(ExecuteMethod.execute_sql_columnIndexes, new Object[]{sql, columnIndexes}, true);
		ResultSetHandler handler = connection.executeSql(statementCreateCommand, command, null, this);
		return handler.isResultType();
	}

	@Override
	public boolean execute(String sql, String[] columnNames) throws SQLException {
		checkClosed();
		clearLastResultSet();
		ExecuteCommand command = new ExecuteCommand(ExecuteMethod.execute_sql_columnNames, new Object[]{sql, columnNames}, true);
		ResultSetHandler handler = connection.executeSql(statementCreateCommand, command, null, this);
		return handler.isResultType();
	}
	
	/**
	 * 清除上次的执行结果
	 */
	protected void clearLastResultSet(){
		if(currentResultSet != null){
			try{
				currentResultSet.close();
			}catch(Exception e){
				logger.error("exception close last resultSet in statement");
			}finally{
				currentResultSet = null;
				currentUpdateCount = 0;
			}
		}
		if(generatedKeysResultSet != null){
			try{
				generatedKeysResultSet.close();
			}catch(Exception e){
				logger.error("exception close generatedKeysResultSet in statement");
			}finally{
				generatedKeysResultSet = null;
			}
		}
	}

	@Override
	public void close() throws SQLException {
		if (closed) {
			return;
		}
		clearLastResultSet();
		Exception exception = null;
		try {
			if (openedStatement.size() > 0) {
				Iterator<Statement> ite = openedStatement.iterator();
				while(ite.hasNext()){
					try {
						ite.next().close();
					} catch (SQLException e) {
						exception = e;
					}
					ite.remove();
				}
			}
		} catch (Exception e) {
			exception = e;
		} finally {
			openedStatement.clear();
		}
		if (exception != null) {
			throw new shardingException("close TStatement error!", exception);
		}
		closed = true;
	}

	protected void checkClosed() throws SQLException {
		if (closed) {
			throw new SQLException("No operations allowed after statement closed.");
		}
	}

	@Override
	public int getMaxFieldSize() throws SQLException {
		return maxFieldSize;
	}

	@Override
	public void setMaxFieldSize(int max) throws SQLException {
		maxFieldSize = max;
	}

	@Override
	public int getMaxRows() throws SQLException {
		return maxRows;
	}

	@Override
	public void setMaxRows(int max) throws SQLException {
		maxRows = max;
	}
	
	@Override
	public int getQueryTimeout() throws SQLException {
		 return this.queryTimeOut;
	}

	@Override
	public void setQueryTimeout(int seconds) throws SQLException {
		this.queryTimeOut = seconds;
	}

	@Override
	public ResultSet getResultSet() throws SQLException {
		return this.currentResultSet;
	}
	@Override
	public int getUpdateCount() throws SQLException {
		return this.currentUpdateCount;
	}

	@Override
	public SQLWarning getWarnings() throws SQLException {
		return null;
	}

	@Override
	public void clearWarnings() throws SQLException {
		return;
	}

	@Override
	public void setFetchDirection(int direction) throws SQLException {
		if(direction != ResultSet.FETCH_FORWARD){
			throw new UnsupportedOperationException("setFetchDirection noly support ResultSet.FETCH_FORWARD");
		}
		this.direction = direction;
	}

	@Override
	public int getFetchDirection() throws SQLException {
		 return this.direction;
	}

	@Override
	public void setFetchSize(int rows) throws SQLException {
		this.fetchSize = rows;
	}

	@Override
	public int getFetchSize() throws SQLException {
		return this.fetchSize;
	}

	/**
	 * 获取此 Statement 对象生成的 ResultSet 对象的结果集合并发性。
	 * 返回ResultSet.CONCUR_READ_ONLY 或 ResultSet.CONCUR_UPDATABLE
	 */
	@Override
	public int getResultSetConcurrency() throws SQLException {
		return ResultSet.CONCUR_READ_ONLY; // TODO 暂时结果集只读
	}

	/**
	 * 获取此 Statement 对象生成的 ResultSet 对象的结果集合类型
	 * 返回ResultSet.TYPE_FORWARD_ONLY、 ResultSet.TYPE_SCROLL_INSENSITIVE 或 ResultSet.TYPE_SCROLL_SENSITIVE 之一
	 */
	@Override
	public int getResultSetType() throws SQLException {
		return ResultSet.TYPE_FORWARD_ONLY;// 默认,不可滚动
	}

	@Override
	public void addBatch(String sql) throws SQLException {
		// TODO Auto-generated method stub
		 throw new UnsupportedOperationException();
	}

	@Override
	public void clearBatch() throws SQLException {
		// TODO Auto-generated method stub
		throw new UnsupportedOperationException();
	}

	@Override
	public int[] executeBatch() throws SQLException {
		// TODO Auto-generated method stub
		throw new UnsupportedOperationException();
	}

	@Override
	public Connection getConnection() throws SQLException {
		return this.connection;
	}

	/**
	 * 获取此 Statement 对象生成的 ResultSet 对象的结果集合可保存性。
	 * ResultSet.HOLD_CURSORS_OVER_COMMIT 或 ResultSet.CLOSE_CURSORS_AT_COMMIT
	 */
	@Override
	public int getResultSetHoldability() throws SQLException {
		return ResultSet.CLOSE_CURSORS_AT_COMMIT;
	}

	@Override
	public boolean isClosed() throws SQLException {
		return this.closed;
	}

    protected void setCurrentResultSet(TResultSet currentResultSet) {
		this.currentResultSet = currentResultSet;
	}

	protected void setCurrentUpdateCount(int currentUpdateCount) {
		this.currentUpdateCount = currentUpdateCount;
	}

	protected ResultSet getGeneratedKeysResultSet() {
		return generatedKeysResultSet;
	}

	protected void setGeneratedKeysResultSet(ResultSet generatedKeysResultSet) {
		this.generatedKeysResultSet = generatedKeysResultSet;
	}

}
